/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.view.css;

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Splits a text into a sequence of tokens, expands definitions in the token content
 * 
 * @author Walter L�tgenau
 */
 public class Tokenizer {
        static Pattern pattern;
        static Pattern definitionsPattern;
        static HashMap<String, CommandType> commandTypes = new HashMap<String, CommandType>();
        String input;
        Matcher matcher;
        CommandType nextType = CommandType.cssInitial;
        String nextValue = null;
        int position = 0;
        HashMap<String, String> definitions;

        static {
           StringBuilder buf = new StringBuilder("^[ \t]*@(");
            for (int i = 0; i < CssPreProcessor.getInstance().getPreProcessorCommands().length; i++) {
                if (i > 0)
                    buf.append("|");
                buf.append(CssPreProcessor.getInstance().getPreProcessorCommands()[i].getName());
            }
            buf.append(")?(.*)$");

            pattern = Pattern.compile(buf.toString(), Pattern.MULTILINE);
            definitionsPattern = Pattern.compile("@([a-zA-Z]*)");

            for (Command command : CssPreProcessor.getInstance().getPreProcessorCommands())
                commandTypes.put(command.getName(), command.getType());
        }

        public Tokenizer(String input, HashMap<String, String> definitions) {
            this.input = input;
            this.definitions = definitions;
            matcher = pattern.matcher(input);
        }

        /**
         * Is there a next token?
         *
         * @return
         */
        public boolean hasNext() {
            return matcher.find();
        }

        /**
         * Return the next token.
         * 
         * @return
         */
        public Token next() {
            Token result = new Token(nextType, nextValue,
                    expandDefinitions(
                            trimLeft(input.substring(position, matcher.start()))));

            position = matcher.end();
            nextType = commandTypes.get(matcher.group(1));

            nextValue = matcher.group(2).trim();

            return result;
        }

        /**
         * Expand the variables that are part of the CSS content.
         * 
         * @param content
         * @return
         */
        protected String expandDefinitions(String content) {
            Matcher definitionsMatcher = definitionsPattern.matcher(content);

            StringBuffer buf = new StringBuffer();
            while (definitionsMatcher.find()) {
                String definition = definitionsMatcher.group(1);
                String replacement = definitions.get(definition);

                if (replacement != null)
                    definitionsMatcher.appendReplacement(buf, replacement);
                else
                    definitionsMatcher.appendReplacement(buf, definitionsMatcher.group(0));
            }
            definitionsMatcher.appendTail(buf);

            return buf.toString();
        }

        protected String trimLeft(String input) {
            int i = 0;
            while(i < input.length() && Character.isWhitespace(input.charAt(i)))
                i++;
            return input.substring(i);
        }

        public String tail() {
            return expandDefinitions(trimLeft(this.input.substring(position)));
        }
    }
